package rexsee.security;

import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import rexsee.core.application.RexseeApplication;
import rexsee.core.browser.Browser;
import rexsee.core.browser.clazz.JavascriptInterface;
import rexsee.core.utilities.Utilities;

public class RexseeSignature implements JavascriptInterface {

	public static final String INTERFACE_NAME = "Signature";
	@Override
	public String getInterfaceName() {
		return mBrowser.application.resources.prefix + INTERFACE_NAME;
	}
	@Override
	public JavascriptInterface getInheritInterface(Browser childBrowser) {
		return this;
	}
	@Override
	public JavascriptInterface getNewInterface(Browser childBrowser) {
		return new RexseeSignature(childBrowser);
	}

	private final Browser mBrowser;

	public RexseeSignature(Browser browser) {
		mBrowser = browser;
	}

	private static PrivateKey string2privateKey_(String algorithm, String key) {
		try {
			return KeyFactory.getInstance(algorithm).generatePrivate(new PKCS8EncodedKeySpec(RexseeSecurity.decodeKey(key)));
		} catch (Exception e) {
			return null;
		}
	}
	private static PublicKey string2publicKey_(String algorithm, String key) {
		try {
			return KeyFactory.getInstance(algorithm).generatePublic(new X509EncodedKeySpec(RexseeSecurity.decodeKey(key)));
		} catch (Exception e) {
			return null;
		}
	}

	private String signRSA_(String algorithm, String privateKey, String string, boolean isFile) {
		byte[] data = (isFile) ? Utilities.getContent(string) : string.getBytes();
		if (data == null) return RexseeApplication.EXCEPTION_PREFIX + "Error on reading source data.";
		try {
			Signature signature = Signature.getInstance(algorithm);
			RSAPrivateKey key = (RSAPrivateKey) string2privateKey_(RexseeSecurity.ALGORITHM_RSA, privateKey);
			signature.initSign(key);
			signature.update(data);
			return RexseeSecurity.bytes2hex(signature.sign());
		} catch (Exception e) {
			return RexseeApplication.EXCEPTION_PREFIX + e.getLocalizedMessage();
		}
	}
	private String signDSA_(String algorithm, String privateKey, String string, boolean isFile) {
		byte[] data = (isFile) ? Utilities.getContent(string) : string.getBytes();
		if (data == null) return RexseeApplication.EXCEPTION_PREFIX + "Error on reading source data.";
		try {
			Signature signature = Signature.getInstance(algorithm);
			DSAPrivateKey key = (DSAPrivateKey) string2privateKey_(RexseeSecurity.ALGORITHM_DSA, privateKey);
			signature.initSign(key);
			signature.update(data);
			return RexseeSecurity.bytes2hex(signature.sign());
		} catch (Exception e) {
			return RexseeApplication.EXCEPTION_PREFIX + e.getLocalizedMessage();
		}
	}
	private boolean verifyRSA_(String algorithm, String publicKey, String signatureString, String string, boolean isFile) {
		byte[] data = (isFile) ? Utilities.getContent(string) : string.getBytes();
		if (data == null) return false;
		byte[] sign = RexseeSecurity.hex2bytes(signatureString);
		if (sign == null) return false;
		try {
			Signature signature = Signature.getInstance(algorithm);
			RSAPublicKey key = (RSAPublicKey) string2publicKey_(RexseeSecurity.ALGORITHM_RSA, publicKey);
			signature.initVerify(key);
			signature.update(data);
			return signature.verify(sign);
		} catch (Exception e) {
			return false;
		}
	}
	private boolean verifyDSA_(String algorithm, String publicKey, String signatureString, String string, boolean isFile) {
		byte[] data = (isFile) ? Utilities.getContent(string) : string.getBytes();
		if (data == null) return false;
		byte[] sign = RexseeSecurity.hex2bytes(signatureString);
		if (sign == null) return false;
		try {
			Signature signature = Signature.getInstance(algorithm);
			DSAPublicKey key = (DSAPublicKey) string2publicKey_(RexseeSecurity.ALGORITHM_DSA, publicKey);
			signature.initVerify(key);
			signature.update(data);
			return signature.verify(sign);
		} catch (Exception e) {
			return false;
		}
	}

	//JavaScript interface

	public String signWithRSA(String algorithm, String privateKey, String string) {
		return signRSA_(algorithm, privateKey, string, false);
	}
	public String signFileWithRSA(String algorithm, String privateKey, String path) {
		return signRSA_(algorithm, privateKey, path, true);
	}
	public String signWithDSA(String algorithm, String privateKey, String string) {
		return signDSA_(algorithm, privateKey, string, false);
	}
	public String signFileWithDSA(String algorithm, String privateKey, String path) {
		return signDSA_(algorithm, privateKey, path, true);
	}

	public boolean verifyWithRSA(String algorithm, String publicKey, String signatureString, String string) {
		return verifyRSA_(algorithm, publicKey, signatureString, string, false);
	}
	public boolean verifyFileWithRSA(String algorithm, String publicKey, String signatureString, String path) {
		return verifyRSA_(algorithm, publicKey, signatureString, path, true);
	}
	public boolean verifyWithDSA(String algorithm, String publicKey, String signatureString, String string) {
		return verifyDSA_(algorithm, publicKey, signatureString, string, false);
	}
	public boolean verifyFileWithDSA(String algorithm, String publicKey, String signatureString, String path) {
		return verifyDSA_(algorithm, publicKey, signatureString, path, true);
	}

}
